Skip to content

Update GetRangeFromAssertions to handle some basic TYP_LONG scenarios where it FitsIn<int32_t>#128906

Open
tannergooding wants to merge 2 commits into
dotnet:mainfrom
tannergooding:better-rngchk2
Open

Update GetRangeFromAssertions to handle some basic TYP_LONG scenarios where it FitsIn<int32_t>#128906
tannergooding wants to merge 2 commits into
dotnet:mainfrom
tannergooding:better-rngchk2

Conversation

@tannergooding
Copy link
Copy Markdown
Member

@tannergooding tannergooding commented Jun 2, 2026

This is an alternative to #128676. It needs confirmation that the diffs/TP is acceptable and may require a few iterations or pulling back prior to it being ready for review.

Copilot AI review requested due to automatic review settings June 2, 2026 16:45
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Jun 2, 2026
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR broadens assertion-based range derivation in the JIT so it can reason about some TYP_LONG value numbers when the resulting values are known to fit in int32, and wires the updated API through rangecheck and assertion propagation to enable additional folding / bounds-check reasoning.

Changes:

  • Update ValueNumStore::IsVNIntegralConstant to coerce constants as int64_t, allowing TYP_LONG constants that fit to be recognized as int32 constants.
  • Extend RangeCheck::GetRangeFromAssertions/worker to accept an explicit var_types and add limited handling for TYP_LONG scenarios (notably RSZ/RSH shift cases and other VN ops).
  • Update assertion propagation and range analysis callsites to pass the expression type and tolerate unknown ranges where TYP_LONG can’t be represented as an int32-based Range.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/coreclr/jit/valuenum.h Enables integral-constant extraction from TYP_LONG VNs via int64_t coercion.
src/coreclr/jit/rangecheck.h Updates GetRangeFromAssertions signature and adds Range::IsUnknown() helper.
src/coreclr/jit/rangecheck.cpp Implements the new typed assertion-range logic and extends range computation to consult it in more cases.
src/coreclr/jit/assertionprop.cpp Adapts assertion-prop folding to the new API and to possibly-unknown ranges for wider types.

Comment thread src/coreclr/jit/rangecheck.cpp
Copilot AI review requested due to automatic review settings June 2, 2026 19:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

Comments suppressed due to low confidence (1)

src/coreclr/jit/rangecheck.cpp:796

  • In VNF_Cast handling, when result is non-constant (e.g., casting to/from types that Range can’t represent) the code unconditionally propagates castOpRange if it’s a constant range. This is unsound for sign-changing casts (e.g., int -> uint, uint -> long) when the operand range can include negatives: the cast changes negative values to large positives, but castOpRange would still contain negatives and exclude the large values.

This can cause incorrect tightening and downstream folding/removal based on a range that doesn’t describe the cast result.

                // Now see if we can do better by looking at the cast source.
                // if its range is within the castTo range, we can use that (and the cast is basically a no-op).
                if (varTypeIsIntegral(arg0Typ))
                {
                    Range castOpRange =
                        GetRangeFromAssertionsWorker(comp, arg0Typ, arg0VN, assertions, --budget, visited);

                    if (castOpRange.IsConstantRange())
                    {
                        if (!result.IsConstantRange())
                        {
                            result = castOpRange;
                        }
                        else if ((castOpRange.LowerLimit().GetConstant() >= result.LowerLimit().GetConstant()) &&
                                 (castOpRange.UpperLimit().GetConstant() <= result.UpperLimit().GetConstant()))
                        {
                            result = castOpRange;
                        }
                    }

Comment thread src/coreclr/jit/rangecheck.h
@tannergooding
Copy link
Copy Markdown
Member Author

tannergooding commented Jun 2, 2026

Extracted two parts out of this into #128922 and #128923 to get better TP and diff metrics to decide how much to preserve or not. Most of this change requires things working together to fully lightup, otherwise we only get relatively small diffs for any singular portion.

tannergooding added a commit that referenced this pull request Jun 3, 2026
…ent/symbolic cases (#128922)

This is a smaller change from
#128906 that doesn't involve more
complex handling around `TYP_LONG`
Copilot AI review requested due to automatic review settings June 4, 2026 00:34
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread src/coreclr/jit/rangecheck.cpp
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

Comment thread src/coreclr/jit/rangecheck.cpp
@tannergooding
Copy link
Copy Markdown
Member Author

/azp run fuzzlyn, runtime-coreclr jitstress, runtime-coreclr jitstressregs

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 3 pipeline(s).

@tannergooding
Copy link
Copy Markdown
Member Author

Diffs are here.

Linux Arm64

Overall (-476,080 bytes)
FullOpts (-476,080 bytes)

Linux x64

Overall (-432,892 bytes)
FullOpts (-432,892 bytes)

Windows Arm64

Overall (-393,596 bytes)
FullOpts (-393,596 bytes)

Windows x64

Overall (-260,911 bytes)
FullOpts (-260,911 bytes)

Linux arm

Overall (-83,344 bytes)
FullOpts (-83,344 bytes)

Windows x86

Overall (-46,131 bytes)
FullOpts (-46,131 bytes)

Linux x64

Overall (+0.05% to +0.18%)
FullOpts (+0.05% to +0.20%)

Windows arm64

Overall (+0.08% to +0.25%)
FullOpts (+0.08% to +0.26%)

Windows x64

Overall (+0.07% to +0.25%)
FullOpts (+0.07% to +0.25%)

@tannergooding
Copy link
Copy Markdown
Member Author

Overall the diffs are teh standard set you'd expect. We have places that change from sign-extension to zero-extension because we know its never negative and we have removal of code that is now provably dead, unreachable, or unnecessary.

This lights up for places where we're explicitly using long or nint on 64-bit platforms, including places like TensorPrimitives, BigInteger, and the various SpanHelpers where we extend the length up to nint to do the rest of the algorithm.

}
else if ((elementCount == 32) && varTypeIsLong(rangeType))
{
return {SymbolicIntegerValue::Zero, UpperBoundForType(TYP_UINT)};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not just rangeType = TYP_UINT like above?

*isKnownNonNegative = true;
}
if ((rng.LowerLimit().GetConstant() > 0) || (rng.UpperLimit().GetConstant() < 0))
Range rng = RangeCheck::GetRangeFromAssertions(this, tree->TypeGet(), treeVN, assertions);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't like the fact we need to pass type. It should be evaluated from VN, shouldn't it?

if ((rng.LowerLimit().GetConstant() > 0) || (rng.UpperLimit().GetConstant() < 0))
Range rng = RangeCheck::GetRangeFromAssertions(this, tree->TypeGet(), treeVN, assertions);

if (rng.IsConstantRange())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's unfortunate we broke the contract for GetRangeFromAssertions to always return a constant range. My opinion we shouldn't do it and instead either upgrade Range to TYP_LONG or introduce a new GetRangeFromAssertions for 64-bit ranges

}
else
{
// TODO: We could return `0, keUnknown` for `elementCount == 32` if the result is TYP_LONG
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't understand what 0, keUnknown is supposed to mean, keUnknown implies it can be something that overflows making the range invalid


bool IsUnknown() const
{
return lLimit.IsUnknown() && uLimit.IsUnknown();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems unused

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants